#include "ibas_in.h"

static void ReloadLog( struct IbasEnv *p_env )
{
	char		*file_content = NULL ;
	int		file_len ;
	ibas_conf	ibas_conf ;
	char		service_name[ LOG_MAXLEN_CUST_LABEL + 1 ] ;
	
	int		nret = 0 ;
	
	file_content = StrdupEntireFile( p_env->ibas_conf_pathfilename , & file_len ) ;
	if( file_content == NULL )
	{
		ERRORLOGSG( "StrdupEntireFile[%s] failed" , p_env->ibas_conf_pathfilename );
		return;
	}
	
	memset( & ibas_conf , 0x00 , sizeof(ibas_conf) );
	nret = DSCDESERIALIZE_JSON_ibas_conf( "GB18030" , file_content , & file_len , & ibas_conf ) ;
	free( file_content );
	if( nret )
	{
		ERRORLOGSG( "DSCDESERIALIZE_JSON_ibas_conf[%s] failed[%d]" , p_env->ibas_conf_pathfilename , nret );
		return;
	}
	
	strcpy( p_env->ibas_conf.ibas.log.iblog_server , ibas_conf.ibas.log.iblog_server );
	strcpy( p_env->ibas_conf.ibas.log.event_output , ibas_conf.ibas.log.event_output );
	strcpy( p_env->ibas_conf.ibas.log.worker_loglevel , ibas_conf.ibas.log.worker_loglevel );
	strcpy( p_env->ibas_conf.ibas.log.worker_output , ibas_conf.ibas.log.worker_output );
	strcpy( p_env->ibas_conf.ibas.log.app_loglevel , ibas_conf.ibas.log.app_loglevel );
	strcpy( p_env->ibas_conf.ibas.log.app_output , ibas_conf.ibas.log.app_output );
	
	INFOLOGSG( "InitLogEnv iblog_server[%s] event_output[%s] worker_output[%s][%s] app_output[%s][%s]" , p_env->ibas_conf.ibas.log.iblog_server , p_env->ibas_conf.ibas.log.event_output , p_env->ibas_conf.ibas.log.worker_loglevel , p_env->ibas_conf.ibas.log.worker_output , p_env->ibas_conf.ibas.log.app_loglevel , p_env->ibas_conf.ibas.log.app_output );
	IBPCleanLogEnv();
	memset( service_name , 0x00 , sizeof(service_name) );
	SNPRINTF( service_name , sizeof(service_name)-1 , "ibas.worker_%d" , p_env->p_worker_detail->index );
	IBPInitLogEnv( p_env->ibas_conf.ibas.log.iblog_server , service_name , p_env->ibas_conf.ibas.log.event_output , IBPConvertLogLevel(ibas_conf.ibas.log.worker_loglevel) , p_env->ibas_conf.ibas.log.worker_output , p_env->p_worker_detail->index );
	IBPChangeAppLogLevel( IBPConvertLogLevel(p_env->ibas_conf.ibas.log.app_loglevel) );
	INFOLOGSG( "InitLogEnv iblog_server[%s] event_output[%s] worker_output[%s][%s] app_output[%s][%s]" , p_env->ibas_conf.ibas.log.iblog_server , p_env->ibas_conf.ibas.log.event_output , p_env->ibas_conf.ibas.log.worker_loglevel , p_env->ibas_conf.ibas.log.worker_output , p_env->ibas_conf.ibas.log.app_loglevel , p_env->ibas_conf.ibas.log.app_output );
	
	return;
}

static int ReceiveRequestAndCallAppAndSendResponse( struct IbasEnv *p_env )
{
	struct tm		stime ;
	
	fd_set			readfds ;
	struct timeval		select_timeval ;
	
	int			nret = 0 ;
	
	ResetHttpSecureEnv( p_env->accepted_session.http_secure_env );
	
	while(1)
	{
		nret = IBMACheckConfigSpaceAttaching( p_env->ibma_config_space ) ;
		if( nret < 0 )
		{
			FATALLOGSG( "IBMACheckConfigSpaceAttaching failed[%d]" , nret );
			return nret;
		}
		
		if( p_env->p_worker_detail->cmd == IBAS_WORKERCMD_RELOAD_LOG )
		{
			p_env->p_worker_detail->cmd = IBAS_WORKERSTATUS_NULL ;
			ReloadLog( p_env );
		}
		
		nret = OnProcessSoCacheChanged( p_env ) ;
		if( nret != HTTP_OK )
		{
			FATALLOGSG( "OnProcessSoCacheChanged failed[%d]" , nret );
			return -1;
		}
		
		p_env->accepted_session.response_flag = 0 ;
		
		DEBUGLOGSG( "worker status [%c]->[%c]" , p_env->p_worker_detail->status , IBAS_WORKERSTATUS_WORKING );
		memset( & (p_env->p_worker_detail->processing_info) , 0x00 , sizeof(struct IbasProcessingInfo) );
		gettimeofday( & (p_env->p_worker_detail->processing_info.timeval_stat.receiving_request) , NULL );
		DiffTimeval( & (p_env->p_worker_detail->processing_info.timeval_stat.waiting_for_accepting) , & (p_env->p_worker_detail->processing_info.timeval_stat.receiving_request) , & (p_env->p_worker_detail->processing_info.timeval_stat.waiting_for_accepting_elapse) );
		LOCALTIME( p_env->p_worker_detail->processing_info.timeval_stat.receiving_request.tv_sec , stime )
		snprintf( p_env->p_worker_detail->processing_info.timeval_stat.timestamp_str , sizeof(p_env->p_worker_detail->processing_info.timeval_stat.timestamp_str)-1 , "%04d-%02d-%02d %02d:%02d:%02d" , stime.tm_year+1900 , stime.tm_mon+1 , stime.tm_mday , stime.tm_hour , stime.tm_min , stime.tm_sec ) ;
		if( p_env->p_worker_detail->next_timeout > 0 )
		{
			p_env->p_worker_detail->processing_info.timeout = p_env->p_worker_detail->next_timeout ;
			p_env->p_worker_detail->next_timeout = 0 ;
		}
		else
		{
			p_env->p_worker_detail->processing_info.timeout = 60 ;
		}
		p_env->p_worker_detail->status = IBAS_WORKERSTATUS_WORKING ;
		
		SetHttpTimeout( p_env->accepted_session.http , p_env->p_worker_detail->processing_info.timeout );
		ResetHttpEnv( p_env->accepted_session.http );
		ResetHttpSecureEnv( p_env->accepted_session.http_secure_env );
		
		nret = OnReceiveRequest( p_env ) ;
		if( nret > 0 )
		{
			INFOLOGSG( "OnReceiveRequest return[%d]" , nret );
			break;
		}
		else if( nret < 0 )
		{
			ERRORLOGSG( "OnReceiveRequest failed[%d]" , nret );
			break;
		}
		else
		{
			INFOLOGSG( "OnReceiveRequest ok" );
		}
		
		gettimeofday( & (p_env->p_worker_detail->processing_info.timeval_stat.processing_app) , NULL );
		DiffTimeval( & (p_env->p_worker_detail->processing_info.timeval_stat.receiving_request) , & (p_env->p_worker_detail->processing_info.timeval_stat.processing_app) , & (p_env->p_worker_detail->processing_info.timeval_stat.receiving_request_elapse) );
		
		nret = OnDispatchUri( p_env ) ;
		if( nret < 0 )
		{
			ERRORLOGSG( "OnDispatchUri failed[%d]" , nret );
			break;
		}
		else
		{
			if( nret && nret != HTTP_OK )
			{
				nret = FormatHttpResponseStartLine( nret , p_env->accepted_session.http , 1 , NULL ) ;
				if( nret )
				{
					ERRORLOGSG( "FormatHttpResponseStartLine failed[%d]" , nret );
					break;
				}
			}
			
			INFOLOGSG( "OnDispatchUri return[%d]" , nret );
		}
		
		gettimeofday( & (p_env->p_worker_detail->processing_info.timeval_stat.sending_response) , NULL );
		DiffTimeval( & (p_env->p_worker_detail->processing_info.timeval_stat.processing_app) , & (p_env->p_worker_detail->processing_info.timeval_stat.sending_response) , & (p_env->p_worker_detail->processing_info.timeval_stat.processing_app_elapse) );
		
		if( p_env->accepted_session.response_flag == 0 )
		{
			nret = OnSendResponse( p_env ) ;
			if( nret )
			{
				ERRORLOGSG( "OnSendResponse failed[%d]" , nret );
				break;
			}
			else
			{
				INFOLOGSG( "OnSendResponse ok" );
			}
			
			gettimeofday( & (p_env->p_worker_detail->processing_info.timeval_stat.finish) , NULL );
			DiffTimeval( & (p_env->p_worker_detail->processing_info.timeval_stat.sending_response) , & (p_env->p_worker_detail->processing_info.timeval_stat.finish) , & (p_env->p_worker_detail->processing_info.timeval_stat.sending_response_elapse) );
		}
		
		if( p_env->p_worker_detail->processing_info.output_event_flag )
		{
			static struct NodeSpaceUnit	null_node_unit = { "" } ;
			static struct HostSpaceUnit	null_host_unit = { "" } ;
			static struct AppSpaceUnit	null_app_unit = { "" } ;
			
			char				*STATUS_CODE = NULL ;
			int				STATUS_CODE_LEN ;
			
			if( p_env->p_client_node_unit == NULL )
				p_env->p_client_node_unit = & null_node_unit ;
			if( p_env->p_client_host_unit == NULL )
				p_env->p_client_host_unit = & null_host_unit ;
			if( p_env->p_this_app_unit == NULL )
				p_env->p_this_app_unit = & null_app_unit ;
			
			STATUS_CODE = GetHttpHeaderPtr_STATUSCODE( p_env->accepted_session.http , & STATUS_CODE_LEN );
			
			DiffTimeval( & (p_env->p_worker_detail->processing_info.timeval_stat.receiving_request) , & (p_env->p_worker_detail->processing_info.timeval_stat.finish) , & (p_env->p_worker_detail->processing_info.timeval_stat.total_elapse) );
			
			NOTICELOGSG( "[%s][%s:%d] -> [%s][%s:%d] [%s][%s] [%.*s] | [%06d.%06d] [%06d.%06d][%06d.%06d][%06d.%06d][%06d.%06d]"
					, p_env->p_client_node_unit->node , p_env->p_client_host_unit->ip , p_env->p_client_host_unit->port
					, p_env->p_this_node_unit->node , p_env->listen_session.netaddr.ip , p_env->listen_session.netaddr.port
					, p_env->p_this_app_unit->app
					, p_env->p_this_app_unit->desc
					, STATUS_CODE_LEN , STATUS_CODE
					, p_env->p_worker_detail->processing_info.timeval_stat.total_elapse.tv_sec , p_env->p_worker_detail->processing_info.timeval_stat.total_elapse.tv_usec
					, p_env->p_worker_detail->processing_info.timeval_stat.waiting_for_accepting_elapse.tv_sec , p_env->p_worker_detail->processing_info.timeval_stat.waiting_for_accepting_elapse.tv_usec
					, p_env->p_worker_detail->processing_info.timeval_stat.receiving_request_elapse.tv_sec , p_env->p_worker_detail->processing_info.timeval_stat.receiving_request_elapse.tv_usec
					, p_env->p_worker_detail->processing_info.timeval_stat.processing_app_elapse.tv_sec , p_env->p_worker_detail->processing_info.timeval_stat.processing_app_elapse.tv_usec
					, p_env->p_worker_detail->processing_info.timeval_stat.sending_response_elapse.tv_sec , p_env->p_worker_detail->processing_info.timeval_stat.sending_response_elapse.tv_usec );
		}
		
		DEBUGLOGSG( "worker status [%c]->[%c]" , p_env->p_worker_detail->status , IBAS_WORKERSTATUS_KEEPALIVE );
		p_env->p_worker_detail->status = IBAS_WORKERSTATUS_KEEPALIVE ;
		memset( & (p_env->p_worker_detail->processing_info) , 0x00 , sizeof(struct IbasProcessingInfo) );
		
		if( ! CheckHttpKeepAlive( p_env->accepted_session.http ) )
		{
			INFOLOGSG( "no keep-alive" );
			break;
		}
		
		FD_ZERO( & readfds );
		FD_SET( p_env->accepted_session.netaddr.sock , & readfds );
		select_timeval.tv_sec = 60 ;
		select_timeval.tv_usec = 0 ;
		nret = select( p_env->accepted_session.netaddr.sock+1 , & readfds , NULL , NULL , & select_timeval ) ;
		if( nret == -1 )
		{
			FATALLOGSG( "select failed[%d]" , errno );
			return 1;
		}
		else if( nret == 0 )
		{
			INFOLOGSG( "keep-alive timeout" );
			break;
		}
	}
	
	return 0;
}
	
int AlonerWorker( struct IbasEnv *p_env )
{
	int			nret = 0 ;
	
	IBPCleanLogEnv();
	IBPInitLogEnv( p_env->ibas_conf.ibas.log.iblog_server , "ibas.worker" , p_env->ibas_conf.ibas.log.event_output , IBPConvertLogLevel(p_env->ibas_conf.ibas.log.worker_loglevel) , p_env->ibas_conf.ibas.log.worker_output , p_env->p_worker_detail->index );
	
	INFOLOGSG( "--- ibas.AlonerWorker[%d] begining ---" , p_env->p_worker_detail->index );
	
	p_env->inotify_fd = inotify_init() ;
	if( p_env->inotify_fd == -1 )
	{
		FATALLOGSG( "inotify_init failed , errno[%d]" , errno );
		return -1;
	}
	
	nret = ReceiveRequestAndCallAppAndSendResponse( p_env ) ;
	if( nret )
	{
		ERRORLOGSG( "ReceiveRequestAndCallAppAndSendResponse failed[%d]" , nret );
		return 1;
	}
	
	DEBUGLOGSG( "close sock[%d]" , p_env->accepted_session.netaddr.sock );
	close( p_env->accepted_session.netaddr.sock );
	
	close( p_env->inotify_fd );
	
	INFOLOGSG( "--- ibas.AlonerWorker[%d] end ---" , p_env->p_worker_detail->index );
	
	return 0;
}

int ManagerWorker( struct IbasEnv *p_env )
{
	char			service_name[ LOG_MAXLEN_CUST_LABEL + 1 ] ;
	
	struct sembuf		sembuf ;
	
	socklen_t		addrlen ;
	
	int			nret = 0 ;
	
	IBPCleanLogEnv();
	memset( service_name , 0x00 , sizeof(service_name) );
	SNPRINTF( service_name , sizeof(service_name)-1 , "ibas.worker_%d" , p_env->p_worker_detail->index );
	IBPInitLogEnv( p_env->ibas_conf.ibas.log.iblog_server , service_name , p_env->ibas_conf.ibas.log.event_output , IBPConvertLogLevel(p_env->ibas_conf.ibas.log.worker_loglevel) , p_env->ibas_conf.ibas.log.worker_output , p_env->p_worker_detail->index );
	
	INFOLOGSG( "--- ibas.ManagerWorker[%d] begining ---" , p_env->p_worker_detail->index );
	
	p_env->inotify_fd = inotify_init() ;
	if( p_env->inotify_fd == -1 )
	{
		FATALLOGSG( "inotify_init failed , errno[%d]" , errno );
		return -1;
	}
	
	while(1)
	{
		DEBUGLOGSG( "worker status [%c]->[%c]" , p_env->p_worker_detail->status , IBAS_WORKERSTATUS_IDLE );
		p_env->p_worker_detail->status = IBAS_WORKERSTATUS_IDLE ;
		
		if( p_env->p_worker_detail->cmd == IBAS_WORKERCMD_EXITING )
		{
			p_env->p_worker_detail->cmd = IBAS_WORKERSTATUS_NULL ;
			DEBUGLOGSG( "worker status [%c]->[%c]" , p_env->p_worker_detail->status , IBAS_WORKERSTATUS_EXITING );
			p_env->p_worker_detail->status = IBAS_WORKERSTATUS_EXITING ;
			break;
		}
		else if( p_env->p_worker_detail->cmd == IBAS_WORKERCMD_RELOAD_LOG )
		{
			p_env->p_worker_detail->cmd = IBAS_WORKERSTATUS_NULL ;
			ReloadLog( p_env );
		}
		
		gettimeofday( & (p_env->p_worker_detail->processing_info.timeval_stat.waiting_for_accepting) , NULL );
		
		memset( & sembuf , 0x00 , sizeof(struct sembuf) );
		sembuf.sem_num = 0 ;
		sembuf.sem_op = -1 ;
		sembuf.sem_flg = SEM_UNDO ;
		nret = semop( p_env->accept_mutex.sem.semid , & sembuf , 1 ) ;
		if( nret == -1 )
		{
			FATALLOGSG( "semop[0x%X][%d] failed , errno[%d]" , p_env->accept_mutex.sem.semkey , p_env->accept_mutex.sem.semid , errno );
			return -1;
		}
		else
		{
			DEBUGLOGSG( "semop[0x%X][%d] ok" , p_env->accept_mutex.sem.semkey , p_env->accept_mutex.sem.semid );
		}
		
		if( p_env->p_worker_detail->cmd == IBAS_WORKERCMD_EXITING )
		{
			p_env->p_worker_detail->cmd = IBAS_WORKERSTATUS_NULL ;
			DEBUGLOGSG( "worker status [%c]->[%c]" , p_env->p_worker_detail->status , IBAS_WORKERSTATUS_EXITING );
			p_env->p_worker_detail->status = IBAS_WORKERSTATUS_EXITING ;
			break;
		}
		else if( p_env->p_worker_detail->cmd == IBAS_WORKERCMD_RELOAD_LOG )
		{
			p_env->p_worker_detail->cmd = IBAS_WORKERSTATUS_NULL ;
			ReloadLog( p_env );
		}
		
		DEBUGLOGSG( "worker status [%c]->[%c]" , p_env->p_worker_detail->status , IBAS_WORKERSTATUS_LISTENING );
		p_env->p_worker_detail->status = IBAS_WORKERSTATUS_LISTENING ;
		
		addrlen = sizeof(struct sockaddr) ;
		p_env->accepted_session.netaddr.sock = accept( p_env->listen_session.netaddr.sock , (struct sockaddr *) & (p_env->accepted_session.netaddr.addr) , & addrlen ) ;
		if( p_env->accepted_session.netaddr.sock == -1 )
		{
			FATALLOGSG( "accept failed , errno[%d]" , errno );
			DEBUGLOGSG( "worker status [%c]->[%c]" , p_env->p_worker_detail->status , IBAS_WORKERSTATUS_EXITING );
			p_env->p_worker_detail->status = IBAS_WORKERSTATUS_EXITING ;
			return -1;
		}
		else
		{
			DEBUGLOGSG( "accept ok , sock[%d]" , p_env->accepted_session.netaddr.sock );
			GETNETADDRESS( p_env->accepted_session.netaddr );
		}
		
		memset( & sembuf , 0x00 , sizeof(struct sembuf) );
		sembuf.sem_num = 0 ;
		sembuf.sem_op = 1 ;
		sembuf.sem_flg = SEM_UNDO ;
		nret = semop( p_env->accept_mutex.sem.semid , & sembuf , 1 ) ;
		if( nret == -1 )
		{
			FATALLOGSG( "semop[0x%X][%d] failed , errno[%d]" , p_env->accept_mutex.sem.semkey , p_env->accept_mutex.sem.semid , errno );
			return -1;
		}
		else
		{
			DEBUGLOGSG( "semop[0x%X][%d] ok" , p_env->accept_mutex.sem.semkey , p_env->accept_mutex.sem.semid );
		}
		
		SetHttpNodelay( p_env->accepted_session.netaddr.sock , 1 );
		
		nret = ReceiveRequestAndCallAppAndSendResponse( p_env ) ;
		if( nret )
		{
			ERRORLOGSG( "ReceiveRequestAndCallAppAndSendResponse failed[%d]" , nret );
			return 1;
		}
		
		DEBUGLOGSG( "close sock[%d]" , p_env->accepted_session.netaddr.sock );
		close( p_env->accepted_session.netaddr.sock );
		
		if( p_env->ibas_conf.ibas.mpm.DYNAMIC_WORKERPOOL.max_process_count >= 0 )
		{
			p_env->p_worker_detail->process_count++;
			if( p_env->p_worker_detail->process_count >= p_env->ibas_conf.ibas.mpm.DYNAMIC_WORKERPOOL.max_process_count )
			{
				DEBUGLOGSG( "worker status [%c]->[%c]" , p_env->p_worker_detail->status , IBAS_WORKERSTATUS_RESTARTING );
				p_env->p_worker_detail->status = IBAS_WORKERSTATUS_RESTARTING ;
				break;
			}
		}
	}
	
	close( p_env->inotify_fd );
	DestroySoFileTree( p_env );
	
	INFOLOGSG( "--- ibas.ManagerWorker[%d] end ---" , p_env->p_worker_detail->index );
	
	return 0;
}

